﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Kernel
{
    [ContractVerification(true)]
    public struct Linkage : IEquatable<Linkage>
    {
        public const int Size = Sz.CLR.Int32 + Sz.CLR.Int32;

        public readonly int PreviousId;

        public readonly int NextId;

        public Linkage(int previousId, int nextId)
            : this()
        {
            this.PreviousId = previousId;
            this.NextId = nextId;
        }

        public static bool operator ==(Linkage instance, Linkage other)
        {
            return instance.Equals(other);
        }

        public static bool operator !=(Linkage instance, Linkage other)
        {
            return !instance.Equals(other);
        }

        public bool Equals(Linkage other)
        {
            return
                this.NextId == other.NextId &&
                this.PreviousId == other.PreviousId;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (obj.GetType() != typeof(Linkage))
            {
                return false;
            }

            return this.Equals((Linkage)obj);
        }

        public override int GetHashCode()
        {
            return
                this.NextId.GetHashCode() ^
                this.PreviousId.GetHashCode();
        }

        internal static unsafe Linkage Read(byte[] buffer, int offset)
        {
            Contract.Requires<ArgumentNullException>(buffer != null);
            Contract.Requires<ArgumentOutOfRangeException>(0 <= offset);
            Contract.Requires<ArgumentException>(offset + Size < buffer.Length, KernelExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                var prev = *(int*)p;
                var next = *(int*)p + Sz.CLR.Int32;
                return new Linkage(prev, next);
            }
        }

        internal unsafe void Write(byte[] buffer, int offset)
        {
            Contract.Requires<ArgumentNullException>(buffer != null);
            Contract.Requires<ArgumentOutOfRangeException>(0 <= offset);
            Contract.Requires<ArgumentException>(offset + Size < buffer.Length, KernelExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                *(int*)p = this.PreviousId;
                *(int*)(p + Sz.CLR.Int32) = this.NextId;
            }
        }
    }
}